# encoding:utf-8
import base64
import hashlib
import simplejson as json
import urllib
import asyncio
from concurrent.futures import ThreadPoolExecutor

import tornado.ioloop
import tornado.web
from tornado import gen
from tornado.httpclient import AsyncHTTPClient

# from pikabase import PikaClient, PikaProduct
# from tornado.concurrent import Future
from datetime import timedelta, datetime
import logging
from util import aes
import aioredis
import cx_Oracle as cx


def init_logger(name, filepath, screen=False, level=logging.INFO):
    logger = logging.getLogger(name)
    fmt = '[%(asctime)s %(filename)s:%(funcName)s:%(lineno)d %(levelname)s:%(name)s]%(message)s'
    file_handler = logging.handlers.TimedRotatingFileHandler(filepath, when='midnight', encoding='utf-8')
    log_format = logging.Formatter(fmt=fmt)
    file_handler.setFormatter(log_format)
    logger.addHandler(file_handler)
    if screen:
        handler = logging.StreamHandler()
        handler.setFormatter(log_format)
        logger.addHandler(handler)
    logger.propagate = False
    logger.setLevel(level)
    return logger


def oracle_sync(proc_name, input_json):
    conn = cx.connect('paydb/paydb#8bill@127.0.0.1:10086/helowin')
    cursor = conn.cursor()
    output = cursor.var(cx.STRING)
    cursor.callproc(proc_name, [input_json, output])
    return output.getvalue()


class PayHandler(tornado.web.RequestHandler):
    def __init__(self, *args, **kwargs):
        self.merchantid = None
        self.merchant_info = None
        self.remote_ip = None
        self.aes_key = None
        self.sha_key = None
        super().__init__(*args, **kwargs)

    @gen.coroutine
    def merchant_exit(self, merchantid=None):
        """
        check if merchant exit,and  ip check
        :return:
        """
        if not merchantid:
            self.merchantid = self.get_argument('merchantid')
        else:
            self.merchantid = merchantid
        # redis = yield from self.application.connect_reids()
        redis = self.application.redis
        merchant_info = yield from redis.hgetall(self.merchantid)
        if not merchant_info:
            return False

        self.merchant_info = merchant_info
        x_real_ip = self.request.headers.get("X-Real-IP")
        self.remote_ip = x_real_ip or self.request.remote_ip
        # self.remote_ip = self.remote_ip.strip()
        self.aes_key = merchant_info[b'AESKEY'].decode('utf-8')
        self.sha_key = merchant_info[b'SHAKEY'].decode('utf-8')
        return True

    def check_ip(self):
        ip = json.loads(self.merchant_info[b'IP'].decode('utf-8'))
        if not ip:
            return True
        if self.remote_ip not in ip:
            return False
        return True

    def check_sign(self):
        """
        check sign
        :return:
        """
        merchantid = self.merchantid
        data = self.get_argument('data')
        timestamp = self.get_argument('timestamp')
        # secretkey + merchantid  + data + v + timestamp  + secretkey
        sign = self.get_argument('sign')
        # 获取商户密钥
        sha_key = self.sha_key
        request_obj = {
            'merchantid': merchantid,
            'data': data,
            'sign': sign,
            'timestamp': timestamp
        }
        sign_string = sha_key + request_obj['merchantid'] + request_obj['data'] + request_obj['timestamp'] + sha_key
        sign = hashlib.sha256(sign_string.encode("utf-8")).hexdigest()
        if request_obj['sign'] == sign:
            return True
        else:
            return False

    def decrypt(self):
        """
        decrypt data
        :return:
        """
        data = self.get_argument('data')
        try:
            data += '===='[len(data) % 4:]
            data = base64.urlsafe_b64decode(data)
            crypto = aes.AesEncryption(self.aes_key)
            data = crypto.decrypt(data)
        except Exception as e:
            return None
        return data

    def encrypt(self, data):
        data = json.dumps(data)
        crypto = aes.AesEncryption(self.aes_key)
        data = crypto.encrypt(data)
        return base64.urlsafe_b64encode(data).decode("utf-8")

    def pay_core(self, proc_name, input_json):
        executor = self.application.executor
        loop = self.application.loop
        input_json = json.dumps(input_json)
        result = yield loop.run_in_executor(executor, oracle_sync, proc_name, input_json)
        return json.loads(result)

    def get_channel(self, method):
        if method == 'GZRB.PAY.ALIPAY.C2B':
            return 'http://127.0.0.1:10000/rongbang/compay_pay'
        elif method == "GZRB.PAY.ALIPAY.B2C":
            return 'http://127.0.0.1:10000/rongbang/compay_pay'
        elif method == 'GZRB.PAY.UNIONPAY.ENTRUST':
            return 'http://127.0.0.1:10000/rongbang/batch_transfer'
        elif method == 'GZMS.PAY.UNIONPAY.ENTRUST':
            return 'http://127.0.0.1:10001/minsheng/batch_transfer'
        elif method == 'GZSL.PAY.WEIXIN.C2B':
            return 'http://127.0.0.1:10002/weixin/c2b'
        elif method == 'GZSL.PAY.WEIXIN.B2C':
            return 'http://127.0.0.1:10002/weixin/b2c'
        elif method == 'GDHM.PAY.UNIONPAY.THIRD.C2D':
            return 'http://127.0.0.1:10003/zaixian/c2d'
        elif method == 'GDHM.PAY.UNIONPAY.THIRD.C2D.SMS':
            return 'http://127.0.0.1:10003/zaixian/quicksms'
        elif method == 'BYEL.PAY.UNIONPAY.THIRD.C2D':
            return 'http://127.0.0.1:10004/zaixian/c2d'
        elif method == 'BYEL.PAY.UNIONPAY.THIRD.C2D.SMS':
            return 'http://127.0.0.1:10004/zaixian/quicksms'

    @gen.coroutine
    def get_channel_redis(self, method):
        redis = self.application.redis
        channel_info = yield from redis.hget('channel', method)
        if channel_info:
            return channel_info.decode('utf-8')

    def niti_data(self, notify_data, result):
        encrypt_text = self.encrypt(notify_data)
        merchantid = result["MERCHANTID"]
        timestamp = result.get("RESPTIME", datetime.now().strftime('%Y%m%d%H%M%S'))
        sign_string = self.sha_key + merchantid + encrypt_text + timestamp + self.sha_key
        sign = hashlib.sha256(sign_string.encode("utf-8")).hexdigest()
        request_obj = {
            "merchantid": merchantid,
            "data": encrypt_text,
            "sign": sign,
            "timestamp": timestamp
        }
        return request_obj

    @staticmethod
    def check_ordernumber(data):
        if "ordernumber" not in data:
            return {"message": "缺少ordernumber订单号", "ret": -1}
        if not isinstance(data['ordernumber'], str):
            return {"message": "ordernumber订单号类型必须是字符串", "ret": -1}
        if len(data['ordernumber']) > 32:
            return {"message": "ordernumber订单号长度错误", "ret": -1}
        if ',' in data['ordernumber']:
            return {"message": "ordernumber订单号非法字符", "ret": -1}
        return {}


def pika_result(future, timeout=10):
    try:
        yield gen.with_timeout(timedelta(seconds=timeout), future)
    except gen.TimeoutError:
        return '超时未返回'
    except:
        return '未知错误'
    else:
        return future.result()


@gen.coroutine
def http_result(url, body=None, model="json"):
    http_client = AsyncHTTPClient()
    if model == "json":
        response = yield http_client.fetch(url, raise_error=False, method="POST", body=json.dumps(body),
                                           request_timeout=60)
    else:
        response = yield http_client.fetch(url, raise_error=False, method="POST", request_timeout=60,
                                           body=urllib.parse.urlencode(body))
    return response


class Application(tornado.web.Application):
    def __init__(self, handlers, debug=False):
        # Prepare IOLoop class to run instances on asyncio
        tornado.ioloop.IOLoop.configure('tornado.platform.asyncio.AsyncIOMainLoop')
        self.loop = None
        self.redis = None
        self.executor = None

        super().__init__(handlers, debug=debug)

    def init_with_loop(self, loop, address=('localhost', 12580), password='PayCore#8bill'):
        self.redis = loop.run_until_complete(
            aioredis.create_redis(address, password=password, loop=loop)
        )
        self.loop = loop
        self.executor = ThreadPoolExecutor(max_workers=4)

    @asyncio.coroutine
    def connect_reids(self):
        redis = yield from aioredis.create_redis(('localhost', 12580), password='PayCore#8bill')
        return redis
